#include <stdlib.h>

#include "deplacement.h"
#include "base_donnees.h"
#include "resume_saisie.h"
#include "carte_zone.h"
#include "def_etat_bataille.h"
#include "manipulation_cases.h"
#include "collisions.h"
#include "etat_bataille.h"

#include "constantes_et_macros.h"




/**
 * \fn static void ajouter_cas_a_traiter(struct tableaux_deplacement* tabs,
                                  const int x, const int y)
 * \brief Ajoute un cas a traiter dans des tableaux de deplacement
 *
 * \param[in,out] tabs L'adresse des tableaux concernes
 * \param[in] x decalage horizontal du cas a traiter
 * \param[in] y decalage vertical du cas a traiter
 */
static void ajouter_cas_a_traiter(struct tableaux_deplacement* tabs,
                                  const int x, const int y)
{
    tabs->ecriture->tab[tabs->ecriture->nb_elements].x = x;
    tabs->ecriture->tab[tabs->ecriture->nb_elements].y = y;
    tabs->ecriture->nb_elements++;
}



/**
 * \fn static char voir_si_position_possible(const struct carte_zone* carte,
                                             const struct ennemi* enn,
                                             const int x, const int y,
                                             const struct etat_bataille* bataille,
                                             const struct base_donnees* donnees)
 * \brief Dit si une position relative pour un ennemi est possible
 *        ou si cela causerait une intersection avec un mur ou autre
 *
 * \param[in] carte L'adresse de la carte de la zone
 * \param[in] enn Un pointeur vers l'ennemi concerne
 * \param[in] x Decalage horizontal menant a la position concernee
 * \param[in] y Decalage vertical menant a la position concernee
 * \param[in] bataille L'adresse de l'etat de la bataille
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return VRAI s'il serait possible que l'ennemi se trouve a la position
 *         donnee, FAUX sinon
 */
static char voir_si_position_possible(const struct carte_zone* carte,
                                      const struct ennemi* enn,
                                      const int x, const int y,
                                      const struct etat_bataille* bataille,
                                      const struct base_donnees* donnees)
{
    int pos_x, pos_y;
    int i, j;
    unsigned char taille_ennemi, taille_autre_ennemi;
    char relief_terrain;
    struct ennemi* p_ennemi;

    taille_ennemi = get_taille(donnees, enn);
    pos_y = enn->coord_y + y;
    /* recherche d'elements de decors */
    for(j = 0;  j < taille_ennemi;  j++)
    {
        pos_x = enn->coord_x + x;
        for(i = 0;  i < taille_ennemi;  i++)
        {
            relief_terrain = get_case_constante_grille(carte, pos_x, pos_y)->relief;
            if(relief_terrain == RELIEF_POSITIF ||
               (relief_terrain == RELIEF_NEGATIF && !get_stats_vol(donnees, enn))
               || get_case_destructible(carte, pos_x, pos_y))
                return FAUX;
            pos_x++;
        }
        pos_y++;
    }
    /* recherche d'autres ennemis */
    p_ennemi = bataille->ennemis.prem;
    while(p_ennemi)
    {
        if(p_ennemi != enn)
        {
            taille_autre_ennemi = get_taille(donnees, p_ennemi);
            if(voir_si_intersection(enn->coord_x + x, enn->coord_y + y, taille_ennemi, taille_ennemi,
                                    p_ennemi->coord_x, p_ennemi->coord_y, taille_autre_ennemi, taille_autre_ennemi))
                return FAUX;
        }
            p_ennemi = p_ennemi->suiv;
    }

    return VRAI;
}



/**
 * \fn static unsigned int calculer_score_possiblite_deplacement_contact(
                                const struct tableaux_deplacement* tabs,
                                const struct ennemi* enn,
                                const struct etat_bataille* bataille,
                                const int x,
                                const int y)
 * \brief Pour une position donnee, fourni une valeur entiere qui est
 *        grande si la position n'est pas favorable a l'ennemi, faible sinon
 *
 * \param[in] tabs L'adresse de tableaux de depalcement remplis
 * \param[in] enn Un pointeur vers l'ennemi concerne
 * \param[in] bataille L'adresse de l'etat de la bataille
 * \param[in] x Decalage horizontal menant a la position concernee
 * \param[in] y Decalage vertical menant a la position concernee
 * \return Une valeur faible si la position est ideale, elevee sinon
 *
 * \warning Cette fonction sert pour les ennemis qui attaquent au contact !
 */
static unsigned int calculer_score_possiblite_deplacement_contact(const struct tableaux_deplacement* tabs,
                                                                  const struct ennemi* enn,
                                                                  const struct etat_bataille* bataille,
                                                                  const int x,
                                                                  const int y)
{
    return GET_DISTANCE(tabs, x, y) + MOTIVATION_ENNEMIS
                                      * DISTANCE_ENTRE(enn->coord_x + x,
                                                       enn->coord_y + y,
                                                       bataille->coord_j_x - DECALAGE_X_J,
                                                       bataille->coord_j_y - DECALAGE_Y_J);
}



/**
 * \fn static unsigned int calculer_score_possiblite_deplacement_distance(
                                const struct tableaux_deplacement* tabs,
                                const struct ennemi* enn,
                                const int x,
                                const int y,
                                const unsigned char taille_ennemi)
 * \brief Pour une position donnee, fourni une valeur entiere qui est
 *        grande si la position n'est pas favorable a l'ennemi, faible sinon
 *
 * \param[in] tabs L'adresse de tableaux de depalcement remplis
 * \param[in] enn Un pointeur vers l'ennemi concerne
 * \param[in] x Decalage horizontal menant a la position concernee
 * \param[in] y Decalage vertical menant a la position concernee
 * \param[in] taille_ennemi La taille de l'ennemi concerne
 * \return Une valeur faible si la position est ideale, elevee sinon
 *
 * \warning Cette fonction sert pour les ennemis qui attaquent a distance !
 */
static unsigned int calculer_score_possiblite_deplacement_distance(const struct tableaux_deplacement* tabs,
                                                                   const struct ennemi* enn,
                                                                   const int x,
                                                                   const int y,
                                                                   const unsigned char taille_ennemi)
{
    unsigned int distance_min, distance;
    int i;
    int decalage;

    /*
     * il faut avoir les coordonnees du centre de l'ennemi pour calculer
     * convenablement les distances, d'autant plus que les
     * endroits propices sont en contact direct avec des murs
     */
    decalage = taille_ennemi / 2;
    distance_min = INF;
    for(i = 0;  i < 4;  i++)
    {
        distance = DISTANCE_ENTRE(enn->coord_x + x + decalage,
                                  enn->coord_y + y + decalage,
                                  tabs->positions_ideales[i].x,
                                  tabs->positions_ideales[i].y);
        if(distance < distance_min || distance_min == INF)
            distance_min = distance;
    }

    return 10 * distance_min + GET_DISTANCE(tabs, x, y);
}



/**
 * \fn static char trouver_premier_deplacement(const struct tableaux_deplacement* tabs,
                                               int x, int y)
 * \brief Permet de retrouver le premier deplacement du
 *        trajet qui permet de se rendre a une position donnee
 *
 * \param[in] tabs L'adresse des tableaux de deplacement (remplis)
 * \param[in] x Decalage horizontal menant a la position concernee
 * \param[in] y Decalage vertical menant a la position concernee
 * \return NEUTRE, HAUT, DROITE, BAS ou GAUCHE, selon la
 *         direction du premier deplacement
 */
static char trouver_premier_deplacement(const struct tableaux_deplacement* tabs,
                                        int x, int y)
{
    char derniere_direction, direction;
    int x_debut, y_debut;
    x_debut = x;
    y_debut = y;

    derniere_direction = NEUTRE;
    while((direction = GET_DIRECTION(tabs, x, y)) != NEUTRE)
    {
        derniere_direction = direction;
        /*
         * on fait le chemin en sens inverse, il
         * faut donc aller dans la direction opposee
         */
        switch(direction)
        {
            case HAUT:
                y++;
                break;
            case DROITE:
                x--;
                break;
            case BAS:
                y--;
                break;
            case GAUCHE:
                x++;
                break;
            default:
                break;
        }
    }

    return derniere_direction;
}



/**
 * \fn static char trouver_meilleure_direction_contact(const struct tableaux_deplacement* tabs,
                                                       const struct ennemi* enn,
                                                       const struct etat_bataille* bataille,
                                                       const struct carte_zone* carte)
 * \brief Permet de trouver la direction a prendre pour un ennemi donne
 *
 * \param[in] tabs L'adresse de tableaux de deplacement remplis
 * \param[in] enn Un pointeur vers l'ennemi concerne
 * \param[in] bataille L'adresse de l'etat de la bataille
 * \param[in] carte L'adresse de la carte
 * \return NEUTRE, HAUT, DROITE, BAS, GAUCHE, selon la meilleure direction a prendre
 *
 * \warning Cette fonction sert pour les ennemis qui attaquent au contact !
 */
static char trouver_meilleure_direction_contact(const struct tableaux_deplacement* tabs,
                                                const struct ennemi* enn,
                                                const struct etat_bataille* bataille,
                                                const struct carte_zone* carte)
{
    /* score le plus avantageux rencontre */
    int min;
    /* coordonnees du score "min" */
    int x_min, y_min;
    int i, j;
    int abs_j;
    int distance;
    int score;

    min = INF;
    x_min = 0;
    y_min = 0;
    for(j = -NB_IT;  j <= NB_IT;  j++)
    {
        abs_j = abs(j);
        for(i = abs_j - NB_IT;  i <= NB_IT - abs_j;  i++)
        {
            distance = GET_DISTANCE(tabs, i, j);
            if(distance == INF)
                continue;
            score = calculer_score_possiblite_deplacement_contact(tabs, enn, bataille, i, j);
            if(min == INF || score < min)
            {
                min = score;
                x_min = i;
                y_min = j;
            }
        }
    }

    return trouver_premier_deplacement(tabs, x_min, y_min);
}



/**
 * \fn static char trouver_meilleure_direction_distance(const struct tableaux_deplacement* tabs,
                                                        const struct ennemi* enn,
                                                        const struct carte_zone* carte,
                                                        const struct base_donnees* donnees)
 * \brief Permet de trouver la direction a prendre pour un ennemi donne
 *
 * \param[in] tabs L'adresse de tableaux de deplacement remplis
 * \param[in] enn Un pointeur vers l'ennemi concerne
 * \param[in] carte L'adresse de la carte
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return NEUTRE, HAUT, DROITE, BAS, GAUCHE, selon la meilleure direction a prendre
 *
 * \warning Cette fonction sert pour les ennemis qui attaquent a distance !
 */
static char trouver_meilleure_direction_distance(const struct tableaux_deplacement* tabs,
                                                 const struct ennemi* enn,
                                                 const struct carte_zone* carte,
                                                 const struct base_donnees* donnees)
{
    /* score le plus avantageux rencontre */
    int min;
    /* coordonnees du score "min" */
    int x_min, y_min;
    int i, j;
    int abs_j;
    int distance;
    int score;
    unsigned char taille_ennemi;

    taille_ennemi = get_taille(donnees, enn);
    min = INF;
    x_min = 0;
    y_min = 0;
    for(j = -NB_IT;  j <= NB_IT;  j++)
    {
        abs_j = abs(j);
        for(i = abs_j - NB_IT;  i <= NB_IT - abs_j;  i++)
        {
            distance = GET_DISTANCE(tabs, i, j);
            if(distance == INF)
                continue;
            score = calculer_score_possiblite_deplacement_distance(tabs, enn, i, j, taille_ennemi);
            if(min == INF || score < min)
            {
                min = score;
                x_min = i;
                y_min = j;
            }
        }
    }

    return trouver_premier_deplacement(tabs, x_min, y_min);
}



/**
 * \fn static void incrementer_score_deplacement(unsigned int* score, const unsigned int vitesse_deplacement)
 * \brief Augmente un score de deplacement d'apres les regles fixees par des
 *        constantes et en fonction de la vitesse de l'element concerne
 *
 * \param[in] score L'adresse du score a modifier
 * \param[in] vitesse_deplacement La vitesse de deplacement de l'element concerne
 */
static void incrementer_score_deplacement(unsigned int* score, const unsigned int vitesse_deplacement)
{
    *score += MIN_DEPLACEMENT + COEFF_DEPLACEMENT * vitesse_deplacement;
}



/**
 * \fn static char choisir_direction(struct tableaux_deplacement* tabs,
                                     const struct ennemi* enn,
                                     const struct etat_bataille* bataille,
                                     const struct carte_zone* carte,
                                     const struct base_donnees* donnees)
 * \brief Donne la direction de mouvement a prendre
 *        pour un ennemi donne dans un contexte donne
 *
 * \param[in,out] tabs L'adresse de tableaux de deplacement correctement initialises
 * \param[in] enn Un pointeur vers l'ennemi considere
 * \param[in] bataille L'adresse de l'etat de la bataille en cours
 * \param[in] carte L'adresse de la carte de la zone actuelle
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return HAUT, DROITE, BAS, GAUCHE, ou NEUTRE
 */
static char choisir_direction(struct tableaux_deplacement* tabs,
                              const struct ennemi* enn,
                              const struct etat_bataille* bataille,
                              const struct carte_zone* carte,
                              const struct base_donnees* donnees)
{
/*#define ANCIENNE_VERSION*/
#if defined ANCIENNE_VERSION
    int diff_h, diff_v;

    diff_h = bataille->coord_j_x - enn->coord_x + DECALAGE_X_J;
    diff_v = bataille->coord_j_y - enn->coord_y + DECALAGE_Y_J;
    if(get_type_attaque(donnees, enn) == DISTANCE)
    {
        /* choix de direction pour les attaques a distance */
        if(!diff_h || !diff_v)
            return NEUTRE;
        if(abs(diff_h) > abs(diff_v))
        {
            if(diff_v < 0)
                return HAUT;
            else
                return BAS;
        }
        else
        {
            if(diff_h < 0)
                return GAUCHE;
            else
                return DROITE;
        }
    }
    else
    {
        /* choix de direction pour les attaques au contact */
        if(abs(diff_h) > abs(diff_v))
        {
            if(diff_h < 0)
                return GAUCHE;
            else
                return DROITE;
        }
        else
        {
            if(diff_v < 0)
                return HAUT;
            else
                return BAS;
        }
    }
#else
    int distance_parcourue;
    int distance_case_adjacente;
    int n;
    /*
     * x et y sont des coordonnees relatives par
     * rapport a la position actuelle de l'ennemi
     */
    int x, y;
    char direction;

    reinit_tabs_deplacement(tabs);
    for(distance_parcourue = 1;  distance_parcourue <= NB_IT;  distance_parcourue++)
    {
        echanger_tabs_deplacement(tabs);
        for(n = 0;  n < tabs->lecture->nb_elements;  n++)
        {
            GET_COORD_CAS(x, y, tabs, n);
            direction = GET_DIRECTION(tabs, x, y);
            /* on ne fait pas le cas du retour en arriere */
            if(direction != BAS)
            {
                distance_case_adjacente = GET_DISTANCE(tabs, x, y - 1);
                if(distance_case_adjacente == INF && voir_si_position_possible(carte, enn, x, y - 1, bataille, donnees))
                {
                    SET_DISTANCE_ET_DIRECTION(tabs, x, y - 1, distance_parcourue, HAUT);
                    ajouter_cas_a_traiter(tabs, x, y - 1);
                }
            }
            if(direction != GAUCHE)
            {
                distance_case_adjacente = GET_DISTANCE(tabs, x + 1, y);
                if(distance_case_adjacente == INF && voir_si_position_possible(carte, enn, x + 1, y, bataille, donnees))
                {
                    SET_DISTANCE_ET_DIRECTION(tabs, x + 1, y, distance_parcourue, DROITE);
                    ajouter_cas_a_traiter(tabs, x + 1, y);
                }
            }
            if(direction != HAUT)
            {
                distance_case_adjacente = GET_DISTANCE(tabs, x, y + 1);
                if(distance_case_adjacente == INF && voir_si_position_possible(carte, enn, x, y + 1, bataille, donnees))
                {
                    SET_DISTANCE_ET_DIRECTION(tabs, x, y + 1, distance_parcourue, BAS);
                    ajouter_cas_a_traiter(tabs, x, y + 1);
                }
            }
            if(direction != DROITE)
            {
                distance_case_adjacente = GET_DISTANCE(tabs, x - 1, y);
                if(distance_case_adjacente == INF && voir_si_position_possible(carte, enn, x - 1, y, bataille, donnees))
                {
                    SET_DISTANCE_ET_DIRECTION(tabs, x - 1, y, distance_parcourue, GAUCHE);
                    ajouter_cas_a_traiter(tabs, x - 1, y);
                }
            }
        }
    }
    if(get_type_attaque(donnees, enn) == CONTACT)
        return trouver_meilleure_direction_contact(tabs, enn, bataille, carte);
    else
        return trouver_meilleure_direction_distance(tabs, enn, carte, donnees);
#endif /* ANCIENNE_VERSION */
}



/**
 * \fn static void trouver_endroits_propices(struct tableaux_deplacement* tabs,
                                             const struct etat_bataille* bataille,
                                             const struct carte_zone* carte)
 * \brief Calcule 4 endroits ideals pour tirer sur le joueur
 *
 * \param[out] tabs L'adresse des tableaux de deplacement a mettre a jour
 * \param[in] bataille L'adresse de l'etat actuel de la bataille
 * \param[in] carte L'adresse de la carte de la zone
 */
static void trouver_endroits_propices(struct tableaux_deplacement* tabs,
                                      const struct etat_bataille* bataille,
                                      const struct carte_zone* carte)
{
    int i;
    struct coordonnees nouvelles_coord;
    char obstacle;
    unsigned int distance;

    /*
     * pour trouver les endroits propices aux attaques a distances, on part
     * du joueur et on s'etend autant que possible dans les 4 directions
     */
    for(i = HAUT;  i <= GAUCHE;  i++)
    {
        tabs->positions_ideales[i].x = bataille->coord_j_x;
        tabs->positions_ideales[i].y = bataille->coord_j_y;
        nouvelles_coord = tabs->positions_ideales[i];
        distance = 0;
        obstacle = FAUX;
        do
        {
            switch(i)
            {
                case HAUT:
                    nouvelles_coord.y--;
                    break;
                case DROITE:
                    nouvelles_coord.x++;
                    break;
                case BAS:
                    nouvelles_coord.y++;
                    break;
                case GAUCHE:
                    nouvelles_coord.x--;
                    break;
                default:
                    break;
            }
            if(get_case_constante_grille(carte, nouvelles_coord.x, nouvelles_coord.y)->relief == RELIEF_POSITIF
                || get_case_destructible(carte, nouvelles_coord.x, nouvelles_coord.y))
            {
                obstacle = VRAI;
            }
            else
                tabs->positions_ideales[i] = nouvelles_coord;
            distance++;
        } while(!obstacle && distance < 15);
    }
}



void modifier_coordonnees(unsigned int* x, unsigned int* y, const char direction)
{
    switch(direction)
    {
        case HAUT:
            *y = *y - 1;
            break;
        case DROITE:
            *x = *x + 1;
            break;
        case BAS:
            *y = *y + 1;
            break;
        case GAUCHE:
            *x = *x - 1;
            break;
        default:
            break;
    }
}




void deplacer_joueur(struct etat_bataille* bataille, const struct carte_zone* carte,
                     const struct resume_saisie* saisie, const struct base_donnees* donnees)
{
    char direction;

    /*
     * Explications : a chaque tour de la boucle d'evenements :
     * - si le score de deplacement d'un individu est superieur ou egal
     *   a SEUIL_DEPLACEMENT, il peut bouger. S'il bouge, on retire
     *   SEUIL_DEPLACEMENT a son score de deplacement.
     * - si le score de deplacement est inferieur a SEUIL_DEPLACEMENT, on ajoute
     *   au score MIN_DEPLACEMENT +
     *            COEFF_DEPLACEMENT * <vitesse de deplacement de l'individu>
     *   puis, si cela lui a permi d'atteindre le seuil, il peut bouger dans la
     *   foulee, en payant le seuil, comme dans le cas precedent.
     * Notons que si quelqu'un tente de bouger mais se heurte a un obstacle,
     * son score de deplacement n'est pas reduit.
     */
    direction = saisie->orientation;
    if(bataille->score_deplacement_j < SEUIL_DEPLACEMENT)
        incrementer_score_deplacement(&bataille->score_deplacement_j,
                                      get_vitesse_deplacement_temp(bataille));
    if(direction != NEUTRE)
    {
        /*
         * si une touche directionnelle a ete enfoncee, on
         * tourne le joueur dans la direction correspondante
         */
        bataille->orientation_j = direction;
        if(bataille->score_deplacement_j >= SEUIL_DEPLACEMENT)
        {
            /* le joueur tente de bouger ! */
            /*
             * etant donne que le joueur est repere par rapport a son centre et
             * non a son coin superieur gauche, il faut modifier quelque-peu
             * les coordonnees lors de l'appel a voir_si_deplacement_possible (cette
             * fonction est egalement utilisee pour les ennemis !)
             */
            if(voir_si_deplacement_possible(direction,
                              bataille->coord_j_x + DECALAGE_X_J,
                              bataille->coord_j_y + DECALAGE_Y_J,
                              carte,
                              TAILLE_JOUEUR, bataille, FAUX, donnees))
            {
                modifier_coordonnees(&bataille->coord_j_x, &bataille->coord_j_y, direction);
                bataille->score_deplacement_j -= SEUIL_DEPLACEMENT;
            }
        }
    }
}






void deplacer_projectiles(struct carte_zone* carte,
                          struct etat_bataille* bataille,
                          const struct base_donnees* donnees)
{
    int i, j;
    struct case_destructible* p_case_destructible;
    struct projectile* p_proj;
    struct projectile* temp;
    unsigned char taille_proj;
    char collision;
    /* rectangle qui va etre occupe et ne l'etait pas avant le deplacement */
    unsigned int x_min_nouv, y_min_nouv;
    unsigned char taille_x_nouv, taille_y_nouv;
    /* carre occupe par le projectile avant de se deplacer */
    unsigned int x_min_ancien, y_min_ancien;
    unsigned char cote_ancien;
    /* union des espaces anciennement et nouvellement occupes */
    unsigned int x_min_tout, y_min_tout;
    unsigned char taille_x_tout, taille_y_tout;

    /* les projectiles se deplacent selon les memes regles que les individus */
    p_proj = bataille->projectiles.prem;
    while(p_proj)
    {
        /*
         * il faut verifier les collisions avec des etres vivants dans
         * tout le rectangle forme par les cases qui vont etre et qui
         * etaient occupees. En effet, puisque les etres vivants bougent,
         * si l'on se contente de verifier les cases qui vont etre
         * occupees, on risque de croiser un individu qui se dirige dans
         * la direction opposee et de ne pas le heurter.
         * Attention ! Il faut rechercher les collisions avec les individus
         * meme si le projectile n'avance pas !
         */
        if(p_proj->score_deplacement < SEUIL_DEPLACEMENT)
        {
            incrementer_score_deplacement(&p_proj->score_deplacement,
                                          get_vitesse_deplacement_projectile(donnees, p_proj));
        }
        taille_proj = get_taille_projectile(donnees, p_proj);
        collision = FAUX;
        if(p_proj->score_deplacement >= SEUIL_DEPLACEMENT)
        {
            /* le projectile avance ! */
            /*
             * un projectile heurte une case de decor si et seulement si cette
             * case se trouve dans le rectangle defini comme etant la zone
             * occupee par le projectile apres deplacement et non-occupee
             * avant. Notez que, en ce qui concerne les elements destructibles,
             * il est tout a fait possible que plusieurs d'entre-eux soient
             * endommages lors du meme impact.
             * Apres un impact, quel qu'il soit, un projectile disparait.
             */
            x_min_ancien = p_proj->coord_x;
            y_min_ancien = p_proj->coord_y;
            cote_ancien = taille_proj;
            switch(p_proj->orientation)
            {
                case HAUT:
                    x_min_nouv = p_proj->coord_x,               y_min_nouv = p_proj->coord_y - 1;
                    taille_x_nouv = taille_proj,                taille_y_nouv = 1;
                    x_min_tout = p_proj->coord_x,               y_min_tout = p_proj->coord_y - 1;
                    taille_x_tout = taille_proj,                taille_y_tout = taille_proj + 1;
                    break;
                case DROITE:
                    x_min_nouv = p_proj->coord_x + taille_proj, y_min_nouv = p_proj->coord_y;
                    taille_x_nouv = 1,                          taille_y_nouv = taille_proj;
                    x_min_tout = p_proj->coord_x,               y_min_tout = p_proj->coord_y;
                    taille_x_tout = taille_proj + 1,            taille_y_tout = taille_proj;
                    break;
                case BAS:
                    x_min_nouv = p_proj->coord_x,               y_min_nouv = p_proj->coord_y + taille_proj;
                    taille_x_nouv = taille_proj,                taille_y_nouv = 1;
                    x_min_tout = p_proj->coord_x,               y_min_tout = p_proj->coord_y;
                    taille_x_tout = taille_proj,                taille_y_tout = taille_proj + 1;
                    break;
                case GAUCHE:
                    x_min_nouv = p_proj->coord_x - 1,           y_min_nouv = p_proj->coord_y;
                    taille_x_nouv = 1,                          taille_y_nouv = taille_proj;
                    x_min_tout = p_proj->coord_x - 1,           y_min_tout = p_proj->coord_y;
                    taille_x_tout = taille_proj + 1,            taille_y_tout = taille_proj;
                    break;
                default:
                    printf("Erreur : projectile avec orientation = %d\n",
                           p_proj->orientation);
                    exit(EXIT_FAILURE);
                    break;
            }
            /*
             * on regarde si le rectangle qui va etre occupe alors qu'il ne
             * l'etait pas avant contient du decors
             */
            for(j = y_min_nouv;  j < y_min_nouv + taille_y_nouv;  j++)
                for(i = x_min_nouv;  i < x_min_nouv + taille_x_nouv;  i++)
                {
                    if(get_case_constante_grille(carte, i, j)->relief == RELIEF_POSITIF)
                        collision = VRAI;
                    p_case_destructible = get_case_destructible(carte, i, j);
                    /* si la case contient un element destructible... */
                    if(p_case_destructible)
                    {
                        collision = VRAI;
                        endommager_case_destructible(carte, i, j);
                    }
                }
            modifier_coordonnees(&p_proj->coord_x, &p_proj->coord_y, p_proj->orientation);
            p_proj->score_deplacement -= SEUIL_DEPLACEMENT;
        }
        else
        {
            /*
             * le projectile ne bouge pas. Il faut juste
             * voir si un adversaire se trouve "sur lui"
             */
            x_min_tout = p_proj->coord_x;
            y_min_tout = p_proj->coord_y;
            taille_x_tout = taille_proj;
            taille_y_tout = taille_proj;
        }
        /*
         * si on a rencontre un obstacle, les adversaires se trouvant au niveau
         * de cet obstacle ne seront pas atteints. Par consequent, la zone a
         * inspecter est celle qui etait occupee avant la
         * tentative de deplacement
         */
        if(collision)
        {
            x_min_tout = x_min_ancien;
            y_min_tout = y_min_ancien;
            taille_x_tout = cote_ancien;
            taille_y_tout = cote_ancien;
        }
        if(rechercher_adversaires_dans_zone(bataille, p_proj, x_min_tout, y_min_tout,
                                            taille_x_tout, taille_y_tout, donnees))
        {
            collision = VRAI;
        }
        /*
         * le passage au projectile suivant doit etre gere differemment
         * si on doit detruire le projectile actuel !
         */
        if(collision)
        {
            temp = p_proj->suiv;
            supprimer_projectile(&bataille->projectiles, p_proj);
            p_proj = temp;
        }
        else
            p_proj = p_proj->suiv;
    }
}



void deplacer_ennemis(struct tableaux_deplacement* tabs,
                      struct etat_bataille* bataille,
                      const struct carte_zone* carte,
                      const struct base_donnees* donnees)
{
    struct ennemi* p_ennemi;
    char direction;

    trouver_endroits_propices(tabs, bataille, carte);
    /*
     * le deplacement des ennemis est globalement gere comme celui du joueur.
     * Voir les commentaires de deplacer_joueur
     */
    p_ennemi = bataille->ennemis.prem;
    while(p_ennemi)
    {
        if(p_ennemi->score_deplacement < SEUIL_DEPLACEMENT)
            incrementer_score_deplacement(&p_ennemi->score_deplacement, get_vit_depl(donnees, p_ennemi));
        if(p_ennemi->score_deplacement >= SEUIL_DEPLACEMENT)
        {
            /* on regarde vers ou veut aller l'ennemi */
            direction = choisir_direction(tabs, p_ennemi, bataille, carte, donnees);
            /* puis, s'il souhaite se deplacer, on le tourne dans le bon sens */
            if(direction == NEUTRE)
                p_ennemi->en_mouvement = FAUX;
            else
                p_ennemi->orientation = direction;
            if(voir_si_deplacement_possible(direction,
                                            p_ennemi->coord_x,
                                            p_ennemi->coord_y,
                                            carte,
                                            get_taille(donnees, p_ennemi),
                                            bataille,
                                            get_stats_vol(donnees, p_ennemi),
                                            donnees))
            {
                modifier_coordonnees(&p_ennemi->coord_x, &p_ennemi->coord_y, direction);
                p_ennemi->score_deplacement -= SEUIL_DEPLACEMENT;
                p_ennemi->en_mouvement = VRAI;
            }
            else
                p_ennemi->en_mouvement = FAUX;
        }
        p_ennemi = p_ennemi->suiv;
    }
}



void init_tabs_deplacement(struct tableaux_deplacement* tabs,
                           const struct etat_bataille* bataille,
                           const struct carte_zone* carte)
{
    if(!(tabs->origine_tab_positions =
         (struct elem_tab_deplacement*)malloc
         ((1 + 2 * NB_IT) * (1 + 2 * NB_IT)
          * sizeof(struct elem_tab_deplacement))))
        exit(EXIT_MALLOC);

    tabs->tab_positions = tabs->origine_tab_positions + NB_IT * (2 + 2 * NB_IT);
    tabs->ecriture = &tabs->tab_a;
    tabs->lecture = &tabs->tab_b;
}



void reinit_tabs_deplacement(struct tableaux_deplacement* tabs)
{
    int i, j;
    int decalage;

    /* remplissage du tableau de positions */
    for(i = -NB_IT;  i <= NB_IT;  i++)
    {
        decalage = NB_IT - abs(i);
        for(j = -decalage;  j <= decalage;  j++)
            SET_DISTANCE(tabs, i, j, INF);
    }
    SET_DISTANCE_ET_DIRECTION(tabs, 0, 0, 0, NEUTRE);
    tabs->ecriture->nb_elements = 0;
    tabs->lecture->nb_elements = 0;
    /*
     * on met (0 ; 0) car le premier cas a traiter
     * est la position actuelle de l'ennemi
     */
    ajouter_cas_a_traiter(tabs, 0, 0);
}



void detruire_tabs_deplacement(struct tableaux_deplacement* tabs)
{
    free(tabs->origine_tab_positions);
}



void echanger_tabs_deplacement(struct tableaux_deplacement* tabs)
{
    struct tab_file* temp;

    temp = tabs->lecture;
    tabs->lecture = tabs->ecriture;
    tabs->ecriture = temp;
    tabs->ecriture->nb_elements = 0;
}



